﻿#include "FITKAcousticsVTKGraphObjectSEASubSystem.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkLookupTable.h>
#include <vtkScalarsToColors.h>
#include <vtkMapper.h>

// App
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKAbstractSEASubSys.h"
#include "FITK_Component/FITKAcousticsPhysics/FITKAcousticsAbsPhysicsProp.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITKAcousticsVTKGraphObjectSelect.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEASubSystem::FITKAcousticsVTKGraphObjectSEASubSystem(Acoustics::FITKAbstractSEASubSys* subSys)
        : FITKAcousticsVTKGraphObject3DPost(subSys)
    {
        // Set the layer need to be rendered.
        setRenderLayer(0, 2);
    }

    FITKAcousticsVTKGraphObjectSEASubSystem::~FITKAcousticsVTKGraphObjectSEASubSystem()
    { 
        // Delete vtk objects.
        deleteVtkObj(m_pointsScaled);
        deleteVtkObj(m_points);
        deleteVtkObj(m_ugrid);  

        // Clear data.
        m_nodeIdPointIndexHash.clear();
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::init()
    {
        // Create the grid.
        m_ugrid = vtkUnstructuredGrid::New();
        m_points = vtkPoints::New();
        m_ugrid->SetPoints(m_points);

        // Create the scaled points.
        m_pointsScaled = vtkPoints::New();

        // Create the highlight selector.
        m_highlightSelector = new FITKAcousticsVTKGraphObjectSelect;
        m_addinGraphObjList.push_back(m_highlightSelector);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::generateSEANodes()
    {
        Acoustics::FITKAbstractSEASubSys* subSys = dynamic_cast<Acoustics::FITKAbstractSEASubSys*>(_dataObj);
        if (!subSys || !m_points)
        {
            return;
        }

        // Reset the points.
        resetVtkObj(m_points);
        m_nodeIdPointIndexHash.clear();

        // Get the SEA node list.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = getSEANodesData();
        if (!seaNodes)
        {
            return;
        }

        const QList<int> nodeIds = subSys->getNodeIDs();

        // Get the SEA nodes points data.
        double pos3[3]{ 0., 0., 0. };
        for (const int & nodeId : nodeIds)
        {
            int nodeIndex = seaNodes->getNodeIndexByID(nodeId);

            // Add a ( 0., 0., 0.) point while the node is invalid.
            Core::FITKNode* node = seaNodes->getNodeAt(nodeIndex);
            if (node)
            {
                node->getCoor(pos3);
            }

            int iPt = m_points->InsertNextPoint(pos3);

            // Save the index hash.
            m_nodeIdPointIndexHash.insert(nodeId, iPt);
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::generateScaledSEANodes(double* center)
    {
        if (!m_points || !m_pointsScaled)
        {
            return;
        }

        // Reset the points.
        resetVtkObj(m_pointsScaled);

        double scaleFactor = getScaleFactor();

        // Scale the points with the original points.
        double pos[3]{ 0., 0., 0. };
        for (int i = 0; i < m_points->GetNumberOfPoints(); i++)
        {
            m_points->GetPoint(i, pos);
            for (int j = 0; j < 3; j++)
            {
                pos[j] = (pos[j] - center[j]) * scaleFactor + center[j];
            }

            m_pointsScaled->InsertNextPoint(pos);
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::update(bool forceUpdate)
    {
        Acoustics::FITKAbstractSEASubSys* subSys = dynamic_cast<Acoustics::FITKAbstractSEASubSys*>(_dataObj);
        if (!subSys || !m_ugrid)
        {
            return;
        }

        // Update the visibility.
        updateVisibility();

        // Enable or disable scale mode. 
        bool scaleModeOn = getRuntimeSettingAs<bool>(RTS_KEY_SCALE_MODE, false);
        setEnableScale(scaleModeOn);

        // Update color.
        //@{
        // Get the default color.
        Acoustics::FITKAcousticsAbsPhysicsProp* phyProp = subSys->getPropSectionObj();
        if (phyProp)
        {
            float phyColor3[3]{ 0., 0., 0. };
            phyProp->getColor(phyColor3);
            QColor defaultColor = QColor::fromRgbF(phyColor3[0], phyColor3[1], phyColor3[2]);

            if (m_fActor)
            {
                m_fActor->setColor(defaultColor);
            }
        }
        //@}

        // Return if the 'forceUpdate' is false.
        if (!forceUpdate)
        {
            return;
        }

        // Reset the data.
        resetVtkData();
        
        // Re-generate the grid data.
        generateDataSet(subSys);

        // Update the actors.
        m_ugrid->Modified();
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::highlight(FITKAcousticsVTKCommons::ShapeType type, QColor color)
    {
        // Show the actor and initialize the data.
        if (!m_highlightSelector)
        {
            return;
        }

        // Default highlight all elements.
        if (type == FITKAcousticsVTKCommons::ShapeType::ShapeTypeNone)
        {
            type = FITKAcousticsVTKCommons::ShapeType::MeshElement;
        }

        vtkDataSet* meshDataSet = getMesh(type);
        if (!meshDataSet)
        {
            return;
        }

        QVector<int> indice;
        for (int i = 0; i < meshDataSet->GetNumberOfCells(); i++)
        {
            indice.push_back(i);
        }

        // Check the color.
        if (!color.isValid())
        {
            color = Qt::red;
        }

        m_highlightSelector->setColor(color);
        m_highlightSelector->setSelectData(meshDataSet, indice, type);
        m_highlightSelector->setVisible(getDataVisibility());

        // Save the highlight flag.
        this->Superclass::highlight(type, color);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::disHighlight()
    {
        // Hide the actor only.
        if (m_highlightSelector)
        {
            m_highlightSelector->clearData();
            m_highlightSelector->setVisible(false);
        }

        // Save the highlight flag.
        this->Superclass::disHighlight();
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::advanceHighlight(FITKAcousticsVTKCommons::ShapeType type, QVector<int> indice, QColor color)
    {
        // Save the highlight flag.
        this->Superclass::advanceHighlight(type, indice, color);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::disAdvanceHighlight()
    {
        // Save the highlight flag.
        this->Superclass::disAdvanceHighlight();
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::setPickMode(FITKAcousticsVTKCommons::ShapePickMode mode)
    {
        if (!m_fActor)
        {
            return;
        }

        switch (mode)
        {
        case Exchange::FITKAcousticsVTKCommons::PickAll:
            m_fActor->SetPickable(true);
            break;
        case Exchange::FITKAcousticsVTKCommons::PickNone:
        case Exchange::FITKAcousticsVTKCommons::PickVertex:
        case Exchange::FITKAcousticsVTKCommons::PickEdge:
        case Exchange::FITKAcousticsVTKCommons::PickFace:
        case Exchange::FITKAcousticsVTKCommons::PickSolid:
        case Exchange::FITKAcousticsVTKCommons::PickNode:
        case Exchange::FITKAcousticsVTKCommons::PickElement:     
        default:
            m_fActor->SetPickable(false);
            break;
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::generateGraph()
    {
        // Create grid actor.
        m_fActor = FITKAcousticsVTKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(true);
        m_fActor->GetProperty()->SetRepresentation(2);
        m_fActor->GetProperty()->SetPointSize(0.);
        m_fActor->GetProperty()->SetLineWidth(1.);
        m_fActor->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActor);

        // Initialize the look up table.
        initLookUptable(m_fActor->GetMapper()->GetLookupTable());

        // Set the input data.
        m_fActor->setInputDataObject(m_ugrid);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::setEnableScale(bool isOn)
    {
        if (m_enableScale == isOn)
        {
            return;
        }

        if (!m_ugrid || !m_points || !m_pointsScaled)
        {
            return;
        }

        // Change the points data.
        m_ugrid->SetPoints(isOn ? m_pointsScaled : m_points);

        // Update the grid.
        m_ugrid->Modified();

        // Save the mode.
        m_enableScale = isOn;
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::setPostData(QString arrayName, QHash<QString, float> & data)
    {
        // Reset the flag.
        m_hasValidPostData = false;

        Acoustics::FITKAbstractSEASubSys* subSys = dynamic_cast<Acoustics::FITKAbstractSEASubSys*>(_dataObj);
        if (!subSys || !m_ugrid)
        {
            return;
        }

        // Get the data by name.
        QString dataName = subSys->getDataObjectName();
        double val = data.value(dataName, qQNaN());
        if (!qIsNaN(val))
        {
            m_hasValidPostData = true;
        }

        // Set the data.
        setValueToCellData(m_ugrid, arrayName, val);
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::updateByDataColor()
    {
        if (!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(DATA_COLOR_ARRAY_NAME);

        // Update the look up table.
        lut->SetVectorModeToRGBColors();
        lut->Build();

        // Reset the transparent.
        m_fActor->GetProperty()->SetOpacity(1.);

        // Update the pipeline.
        if (m_ugrid)
        {
            m_ugrid->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::updateByPostCellData(QString arrayName, double rangeMin, double rangeMax)
    {
        if (!m_fActor)
        {
            return;
        }

        // Check the data value.
        if (!m_hasValidPostData)
        {
            updateByPostNullData();
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(arrayName);

        // Update the look up table.
        lut->SetVectorModeToComponent();
        lut->SetVectorComponent(0);
        lut->SetRange(rangeMin, rangeMax);
        lut->Build();

        // Set the scalar range.
        vtkMapper* mapper = m_fActor->GetMapper();
        mapper->SetScalarRange(rangeMin, rangeMax);

        // Reset the transparent.
        m_fActor->GetProperty()->SetOpacity(1.);

        // Update the pipeline.
        if (m_ugrid)
        {
            m_ugrid->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::updateByPostNullData()
    {
        if (!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        // Enable the cell data color array.
        m_fActor->setScalarVisibility(true);
        m_fActor->setScalarModeToUseCellFieldData();
        m_fActor->selectScalarArray(DATA_POST_NULL_ARRAY_NAME);

        // Update the look up table.
        lut->SetVectorModeToRGBColors();
        lut->Build();

        // Set the transparent.
        m_fActor->GetProperty()->SetOpacity(1 - FITKAcousticsVTKCommons::s_defaultNullPostTransparency);

        // Update the pipeline.
        if (m_ugrid)
        {
            m_ugrid->Modified();
        }
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::setEnableLogScale(bool isOn)
    {
        if (!m_fActor)
        {
            return;
        }

        vtkLookupTable* lut = getActorLut(m_fActor);
        if (!lut)
        {
            return;
        }

        lut->SetScale(isOn ? VTK_SCALE_LOG10 : VTK_SCALE_LINEAR);
        lut->Build();
    }

    bool FITKAcousticsVTKGraphObjectSEASubSystem::getDataVisibility()
    {
        return getGlobalSEASubSystemVisibility() && this->Superclass::getDataVisibility();
    }

    Acoustics::FITKAcousticsDiscNodeList* FITKAcousticsVTKGraphObjectSEASubSystem::getSEANodesData()
    {
        // Get the SEA-model data.
        Acoustics::FITKAcousticsDiscModel* discModel = FITKGLODATA->getMeshData<Acoustics::FITKAcousticsDiscModel>();
        if (!discModel)
        {
            return nullptr;
        }

        Acoustics::FITKAcousticsSEAModel* seaModel = discModel->getSEAModel();
        if (!seaModel)
        {
            return nullptr;
        }

        // Get the SEA nodes.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = discModel->getNodes();
        if (!seaNodes)
        {
            return nullptr;
        }

        return seaNodes;
    }

    vtkDataSet* FITKAcousticsVTKGraphObjectSEASubSystem::getMesh(FITKAcousticsVTKCommons::ShapeType type)
    {
        Q_UNUSED(type);
        return m_ugrid;
    }

    double FITKAcousticsVTKGraphObjectSEASubSystem::getScaleFactor()
    {
        // Override in sub system sub-class.
        return 1.;
    }

    bool FITKAcousticsVTKGraphObjectSEASubSystem::getGlobalSEASubSystemVisibility()
    {
        // Get the runtime settings for visibility. 
        bool showSubSys = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SUB_SYSTEMS, true);
        bool showSEASubSys = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_SUBSYS, true);
        bool mainFlag = showSubSys && showSEASubSys;
        return mainFlag;
    }

    void FITKAcousticsVTKGraphObjectSEASubSystem::resetVtkData()
    {
        resetVtkObj(m_points);
        resetVtkObj(m_pointsScaled);
        resetVtkObj(m_ugrid);
        m_nodeIdPointIndexHash.clear();
    }
}   // namespace Exchange